package com.teufandsub.socialproximity.services;

import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.util.Log;

import com.teufandsub.socialproximity.controller.Constants;
import com.teufandsub.socialproximity.controller.Controller;
import com.teufandsub.socialproximity.controller.Utils;
import com.teufandsub.socialproximity.logic.User;

/**
 * @author Bastien Mathias <bastienmathias@gmail.com>
 * @author Antoine Teuf <antoine.teuf@gmail.com>
 * @version 1.0
 * @since 2013-05-22
 * 
 * Class where HTTP calls to the cloud are maid.
 */

public class HttpService {
	

    @SuppressWarnings("unused")
	private final static String CLOUD_APP_URL = (false) ? "http://10.40.12.33:8888/" : "https://social-proxi.appspot.com/";
    private final static String MESSAGE_SERVLET = "socialproximity_cloudapp/messagesService";
    private final static String REGISTRATION_SERVLET = "socialproximity_cloudapp/registrationService";
    private final static String AUTHENTICATION_SERVLET = "socialproximity_cloudapp/authenticationService";
    private final static String RESEARCH_SERVLET = "socialproximity_cloudapp/researchService";
    private final static String LOCATION_SERVLET = "socialproximity_cloudapp/locationService";
    private final static String LISTS_SERVLET = "socialproximity_cloudapp/listService";
    private final static String KEEPACTIVE_SERVLET = "socialproximity_cloudapp/keepActiveService";
    private final static String INTEREST_SERVLET = "socialproximity_cloudapp/interestService";
    private final static String USER_INFORMATION_SERVLET = "socialproximity_cloudapp/userInformationsService";
    
    private HttpPost httpPost = null;
    private HttpGet httpGet = null;
    private HttpClient httpClient = getMyDefaultHttpClient();
    private Context context;
     
    
	public HttpService(Context context) {
		super();
		this.context = context;
	}

	/**
	 * Method to send message to the cloud.
	 * 
	 * @param message {@link JSONObject} Message informations as JSONObject to send to the cloud.
	 * @return JSONObject return value after the sending to handle error.
	 */
	public JSONObject sendMessage(JSONObject message) {
		JSONObject ret=null;
		
		try {
			message.put(Constants.SESSIONID_NAME, Controller.getInstance().getSessionInfo(context).getKey());
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, "Unable to find sessionId to send message");
		}
		
		final String toSend = message.toString();

		httpPost = new HttpPost(CLOUD_APP_URL + MESSAGE_SERVLET);
		try {
			httpPost.setEntity(new StringEntity(toSend, HTTP.UTF_8));
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, e.toString());
		}

		HttpResponse rep = execute(httpPost);
		try {
			ret = Utils.readResponse(rep.getEntity().getContent());
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, "Error during message reading"+e.toString());
		}
		return ret;
	}
	
	/**
	 * Method used to update user informations to the cloud.
	 * 
	 * @param userInfo {@link JSONObject} User informations as JSONObject to send to the cloud.
	 * @return JSONObject return value after the sending to handle error.
	 */
	public JSONObject updateUserInformations(JSONObject userInfo){
		JSONObject ret=null;
		final String toSend = userInfo.toString();
		httpPost = new HttpPost(CLOUD_APP_URL + USER_INFORMATION_SERVLET);
		try {
			httpPost.setEntity(new StringEntity(toSend, HTTP.UTF_8));
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, e.toString());
		}

		HttpResponse rep = execute(httpPost);
		try {
			ret = Utils.readResponse(rep.getEntity().getContent());
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, "Error during message reading"+e.toString());
		}
		return ret;
	}
	
	/**
	 * Method used to register the user to the cloud.
	 * 
	 * @param registrationInfo  {@link JSONObject} Registation informations as JSONObject to send to the cloud.
	 * @return int return value after the sending to handle error.
	 */
	public int registration(JSONObject registrationInfo){
		final String toSend = registrationInfo.toString();
		httpPost = new HttpPost(CLOUD_APP_URL + REGISTRATION_SERVLET);
		try {
			httpPost.setEntity(new StringEntity(toSend, HTTP.UTF_8));
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, e.toString());
		}
		HttpResponse rep = execute(httpPost);
		int ret=-1;
		try {
			JSONObject JSONrep = Utils.readResponse(rep.getEntity().getContent());
			ret = JSONrep.getInt(Constants.CODE_NAME);
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, "Unable to read response : "+e.toString());
		}
		return ret;
	}
	
	/**
	 * Method used to authenticate user to the cloud.
	 * 
	 * @param authenticationInfo {@link JSONObject} authentication informations as JSONObject to send to the cloud.
	 * @return JSONObject return value after the sending to handle error.
	 */
	public JSONObject authentication(JSONObject authenticationInfo){
		final String toSend = authenticationInfo.toString();
		JSONObject ret=null;

		httpPost = new HttpPost(CLOUD_APP_URL + AUTHENTICATION_SERVLET);
		try {
			httpPost.setEntity(new StringEntity(toSend, HTTP.UTF_8));
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, e.toString());
		}
		HttpResponse rep = execute(httpPost);
		try {
			ret = Utils.readResponse(rep.getEntity().getContent());
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, "Error during authentication"+e.toString());
		}
		return ret;
	}
	
	/**
	 * Method used to manage user relation with people.
	 * 
	 * @param info {@link JSONObject} Informations to manage friend to send to the cloud.
	 * @return {@link JSONObject} return value after the sending to handle error.
	 */
	public JSONObject manageFriend(JSONObject info){
		final String toSend = info.toString();
		JSONObject ret=null;
		
		httpPost = new HttpPost(CLOUD_APP_URL + LISTS_SERVLET);
		try {
			httpPost.setEntity(new StringEntity(toSend, HTTP.UTF_8));
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, e.toString());
		}
		HttpResponse rep = execute(httpPost);
		try {
			ret = Utils.readResponse(rep.getEntity().getContent());
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, "Error during authentication"+e.toString());
		}
		return ret;
	}
	
	/**
	 * Method used to get session informations from the cloud.
	 * 
	 * @param sessionId String sessionId of the current user.
	 * @param username String username of the current user.
	 * @param regId String regId of the current user.
	 * @return JSONObject session Informations from the cloud.
	 */
	public JSONObject getSessionInformations(String sessionId, String username, String regId){
		JSONObject ret=null;
		List<NameValuePair> params = new LinkedList<NameValuePair>();
		params.add(new BasicNameValuePair(Constants.SESSIONID_NAME, sessionId));
		params.add(new BasicNameValuePair(User.USERNAME_NAME, username));
		params.add(new BasicNameValuePair(User.REGID_NAME, regId));
		
		httpGet = new HttpGet(CLOUD_APP_URL + AUTHENTICATION_SERVLET+"?"+URLEncodedUtils.format(params, "utf-8"));
		HttpResponse rep = execute(httpGet);
		try {
			ret=Utils.readResponse(rep.getEntity().getContent());
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, "Error during session information retrieve : "+e.toString());
		}
		return ret;
	}
	
	/**
	 * Method to get intersted from the cloud.
	 * 
	 * @param sessionId String sessionId of the current user.
	 * @param username String username of the current user.
	 * @param listOwnerName String name of the user you want to retrieve the list.
	 * @param areLinked boolean value to know if we retrieve linked interest or not.
	 * @return JSONObject list as JSONObject 
	 */
	public JSONObject getInterests(String sessionId, String username, String listOwnerName, boolean areLinked){
		JSONObject ret=null;
		List<NameValuePair> params = new LinkedList<NameValuePair>();
		params.add(new BasicNameValuePair(Constants.SESSIONID_NAME, sessionId));
		params.add(new BasicNameValuePair(User.USERNAME_NAME, username));
		params.add(new BasicNameValuePair(Constants.LISTOWNERNAME_NAME, listOwnerName));
		params.add(new BasicNameValuePair(Constants.ARELINKEDINTEREST_NAME, Boolean.toString(areLinked)));
		
		httpGet = new HttpGet(CLOUD_APP_URL + INTEREST_SERVLET+"?"+URLEncodedUtils.format(params, "utf-8"));
		HttpResponse rep = execute(httpGet);
		try {
			ret=Utils.readResponse(rep.getEntity().getContent());
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, "Error during interests retrieve : "+e.toString());
		}
		return ret;
	}
	
	/**
	 * Method to get friendlist, blacklist and waiting list of the current user.
	 * 
	 * @param sessionId String sessionId of the current user
	 * @param username String username of the current user
	 * @return JSONObject friendlist, blacklist and waiting list as JSONObject.
	 */
	public JSONObject getLists(String sessionId, String username){
		JSONObject ret=null;
		List<NameValuePair> params = new LinkedList<NameValuePair>();
		params.add(new BasicNameValuePair(Constants.SESSIONID_NAME, sessionId));
		params.add(new BasicNameValuePair(User.USERNAME_NAME, username));
		
		httpGet = new HttpGet(CLOUD_APP_URL + LISTS_SERVLET +"?"+URLEncodedUtils.format(params, "utf-8"));
		HttpResponse rep = execute(httpGet);
		try {
			ret=Utils.readResponse(rep.getEntity().getContent());
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, "Error during session information retrieve : "+e.toString());
		}
		return ret;
	}
	
	/**
	 * Method used to get connected user after a research.
	 * 
	 * @param info JSONObject research informations to send to the cloud
	 * @return JSONObject list of users corresponding to the research and are connected.
	 */
	public JSONObject getConnectedUsers(JSONObject info){
		final String toSend = info.toString();
		JSONObject ret=null;

		httpPost = new HttpPost(CLOUD_APP_URL + RESEARCH_SERVLET);
		try {
			httpPost.setEntity(new StringEntity(toSend, HTTP.UTF_8));
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, e.toString());
		}
		HttpResponse rep = execute(httpPost);
		try {
			ret = Utils.readResponse(rep.getEntity().getContent());
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, "Error during authentication"+e.toString());
		}
		return ret;
	}
	
	/**
	 * Method used to update location of the user.
	 * 
	 * @param location JSONObject location informations to send to the cloud.
	 */
	public void updateLocation(JSONObject location){
		final String toSend = location.toString();

		httpPost = new HttpPost(CLOUD_APP_URL + LOCATION_SERVLET);
		try {
			httpPost.setEntity(new StringEntity(toSend, HTTP.UTF_8));
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, e.toString());
		}
		
		execute(httpPost);
		Log.i(Constants.APP_NAME, "Location update");
	}
	
	/**
	 * Method to notify the cloud application that user is active.
	 * 
	 * @param sessionId String SessionId of the current user.
	 * @param username String username of the current user.
	 * @param stayActive boolean value to notify to the cloud we're active or not.
	 */
	public void stayActive(String sessionId, String username, boolean stayActive){
		JSONObject jsonToSend = new JSONObject();
		try {
			jsonToSend.put(User.USERNAME_NAME, username);
			jsonToSend.put(Constants.SESSIONID_NAME, sessionId);
			jsonToSend.put(User.AVAILABLE_NAME, stayActive);
		} catch (JSONException e1) {
			Log.e(Constants.APP_NAME, e1.toString());
		}
		final String toSend = jsonToSend.toString();

		httpPost = new HttpPost(CLOUD_APP_URL + KEEPACTIVE_SERVLET);
		try {
			httpPost.setEntity(new StringEntity(toSend, HTTP.UTF_8));
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, e.toString());
		}
		execute(httpPost);
	}
	
	/**
	 * Method used to add/remove interests to the cloud.
	 * 
	 * @param jsonRequest {@link JSONObject} list of interest to send to the cloud.
	 * @return {@link JSONObject} return value after the sending to handle error.
	 */
	public JSONObject updateInterests(JSONObject jsonRequest){
		final String toSend = jsonRequest.toString();
		JSONObject ret=null;

		httpPost = new HttpPost(CLOUD_APP_URL + INTEREST_SERVLET);
		try {
			httpPost.setEntity(new StringEntity(toSend, HTTP.UTF_8));
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, e.toString());
		}
		HttpResponse rep = execute(httpPost);
		try {
			ret = Utils.readResponse(rep.getEntity().getContent());
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, e.toString());
		}
		return ret;
	}
	
	/**
	 * Method used to execute an HTTPGET call.
	 * 
	 * @param hg HTTPGet Informations for the get call.
	 * @return {@link HttpResponse} return informations of the call.
	 */
	private HttpResponse execute(HttpGet hg){
		final HttpGet httpGet = hg;
			
		HttpResponse rep = null;
		try {
			rep = httpClient.execute(httpGet);
			
		} catch (InterruptedIOException e) {
			Log.e(Constants.APP_NAME, e.toString());
			e.printStackTrace();
		} catch (UnknownHostException e) {
			Log.e(Constants.APP_NAME, e.toString());
			e.printStackTrace();
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, e.toString());
			e.printStackTrace();
		}
		return rep;
		
	}
	

	/**
	 * Method used to do a post call.
	 * 
	 * @param hp HTTPost informations to the post call
	 * @return {@link HttpResponse} return informations of the call.
	 */
	private HttpResponse execute(HttpPost hp){
		final HttpPost httpPost = hp;
			
		HttpResponse rep = null;
		try {
			rep = httpClient.execute(httpPost);
			
		} catch (InterruptedIOException e) {
			Log.e(Constants.APP_NAME, e.toString());
			e.printStackTrace();
		} catch (UnknownHostException e) {
			Log.e(Constants.APP_NAME, e.toString());
			e.printStackTrace();
		} catch (Exception e) {
			Log.e(Constants.APP_NAME, e.toString());
			e.printStackTrace();
		}
		return rep;
		
	}
	
	/**
	 * Method used to get informations to do a call.
	 * 
	 * @return HttpClient Informations for a call to the cloud applications
	 */
    private HttpClient getMyDefaultHttpClient() {
        HttpParams httpParameters = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(httpParameters, 10 * 1000);// Set the timeout in milliseconds until a connection is established.
        HttpConnectionParams.setSoTimeout(httpParameters, 5 * 1000); // Set the default socket timeout (SO_TIMEOUT) in milliseconds which is the timeout for waiting for data.
        DefaultHttpClient httpClient = new DefaultHttpClient(httpParameters);
        return httpClient;
    }
}
